/**
 * @file QWF_Usart.c
 * @brief 串口通信管理模块，实现与VOFA+调试软件的通信功能
 *        包含数据发送、双缓冲DMA接收、命令解析及波形数据传输等功能
 * @author WeiFan Qiu (qiuweifan0523@faxmail.com)
 * @version 1.0
 * @date 2025-08-07
 * 
 * @copyright Copyright (c) 2025  邱伟凡
 * 
 * @par 修改日志:
 * - 2025-08-07: 初始版本，实现基础串口发送、VOFA通信任务及双缓冲DMA接收框架
 * - 2025-08-08: 完善双缓冲回调函数，解决数据错位问题（待测试）
 * @bug 待解决问题:
 * 1. 双缓冲回调函数未启用：vofa_callback函数被注释，需完善中断配置以触发数据解析,原因是因为底盘调用会错位，但是云台不会，改完后还没测试
 * 2. 数据发送可靠性：HAL_UART_Transmit阻塞发送可能在高负载时影响系统实时性
 * 3. 命令模式扩展：当前仅支持"map"命令，需扩展更多指令支持
 * 4. 缓冲区长度验证：QWF_double_buffer_dma_idle_callback中未校验buffer_len有效性
 */
#include "QWF_Usart.h"
#include "freertos.h"		//freertos
#include "task.h"			//freertos暂停时长
#define vofa_huart	huart3		// 定义VOFA通信使用的串口句柄（UART6）
#define vofa_dma	hdma_usart3_rx	// 定义VOFA通信使用的DMA句柄（UART6_RX DMA）
#define send_vofa_end()							Send_Data_To_USART(&vofa_huart,(uint8_t*)&tail,4)
extern UART_HandleTypeDef vofa_huart;
extern DMA_HandleTypeDef vofa_dma;
static vofa_t VOFA;			// VOFA通信状态结构体实例
vofa_t* vofa=&VOFA;			// 指向VOFA结构体的指针，用于外部访问状态
#ifndef debug
	#error	"undef debug"
#endif							// 强制要求定义debug宏，避免未初始化调试模式
static const uint8_t tail[4]={0x00,0x00,0x80,0x7f};	// VOFA数据帧尾（IEEE754浮点数0.0的字节表示，用于标识数据结束）
/**************************串口发送函数**************************************************/
/**
 * @brief 向指定串口发送字节数据
 * @param huart 目标串口句柄（如&huart1）
 * @param data 待发送数据的字节指针
 * @param data_Byte 待发送数据的字节长度,一般用sizeof(data)
 * @note 调试模式下使用阻塞发送（HAL_UART_Transmit），非调试模式不执行发送
 *       注意：阻塞发送可能在无串口连接时导致任务挂起
 * @example	Send_Data_To_USART(&huart6,"hello world",sizeof("hello world"));
 */
void Send_Data_To_USART(UART_HandleTypeDef * huart,uint8_t* data,uint16_t data_Byte ){

	#if debug==QWF_Enable
		//	HAL_UART_Transmit_IT(huart,(uint8_t*)data,data_Byte);
		//	while (HAL_UART_GetState(huart) == HAL_UART_STATE_BUSY_TX);//缺点：若未插对应usart口可能会卡住	
		HAL_UART_Transmit(huart,(uint8_t*)data,data_Byte,0xffff);//缺点：慢
	#else
		return;
	#endif
}
/**
 * @brief  串口发送浮点数（一次只能发一个）
 * @param  串口几，要发送的浮点数
 * @return 无
 */ 
/**
 * @brief 向指定串口发送单精度浮点数（转换为4字节流）
 * @param huart 目标串口句柄
 * @param float_data 待发送的浮点数
 * @note 本质是将浮点数按内存字节直接发送,注意：接收端需按IEEE754解析(vofa自带)
 * @example	Send_float_To_USART(&huart6,123.456f);
 */
static void Send_float_To_USART(UART_HandleTypeDef * huart,float float_data){
	#if debug==QWF_Enable	
		Send_Data_To_USART(huart,(uint8_t*)&float_data,(uint16_t)sizeof(float));
	#else
		return;
	#endif
}
/**
 * @brief 初始化VOFA通信模块（双缓冲DMA接收）
 * @details 调用双缓冲DMA初始化函数，配置UART6的接收缓冲区及空闲中断
 */
void VOFA_Init(void){
	QWF_double_buffer_dma_idle_Init(&vofa_huart,&vofa_dma,vofa->rx.buffer[0],vofa->rx.buffer[1],sizeof(vofa->rx.buffer[0]));
}
/**
 * @brief VOFA数据解析函数（命令模式）
 * @param pData 接收到的串口数据缓冲区指针
 * @details 检测"cmd"关键字进入命令模式，检测"map"关键字返回"can"响应
 */
void vofa_cal(uint8_t* pData){
	if(!memcmp(pData,"cmd",sizeof("cmd"))){//进入cmd模式
		vofa->tx.mode=cmd;
	}
	if(vofa->tx.mode==cmd){
		if(!memcmp(pData,"map",sizeof("map"))){//导航
			Send_Data_To_USART(&vofa_huart,(uint8_t*)"can\n",sizeof("can\n"));
		}
	}
}
/**
 * @brief VOFA串口空闲中断回调函数（待启用）
 * @details 需在UART空闲中断服务函数中调用，处理双缓冲DMA接收数据
 *       当前被注释，需取消注释并配置中断优先级以启用
 */
void vofa_callback(void){
//	QWF_double_buffer_dma_idle_callback(&vofa_huart,&vofa_dma,0,vofa_cal);
}
/**
 * @brief VOFA波形数据发送任务（FreeRTOS任务）
 * @param argument FreeRTOS任务参数（未使用）
 * @details 1ms周期执行，根据模式选择发送波形数据或处理命令
 *       波形模式下循环发送注册的浮点数，结尾添加帧尾标识
 */
void vofa_task(void const * argument){// 周期性发送VOFA波形数据
    static TickType_t xLastWakeTime;
    const TickType_t xPeriod = pdMS_TO_TICKS(1);// 1ms周期
    // 初始化唤醒时间
    xLastWakeTime = xTaskGetTickCount();
	while(1){
		if(vofa->tx.mode==waveform){//在发送波形模式
			for(int i=0;i<vofa->tx.data.index;i++)//遍历注册的浮点数
				Send_float_To_USART(&vofa_huart,*vofa->tx.data.waveform[i].meas_p);
	/***********************结尾*********************************************/
			send_vofa_end();
		}
		else if(vofa->tx.mode==cmd){//在命令模式，停止发送波形，处理命令
			//cmd在中断里处理（一发一收）
		}	
		vTaskDelayUntil(&xLastWakeTime, xPeriod);	
	}
	#if debug!=QWF_Enable
		vTaskDelete(NULL);
	#endif
}
//双缓冲空闲中断相关（初始化+回调函数）
/**
 * @brief 初始化双缓冲DMA+空闲中断接收
 * @param huart 串口句柄
 * @param hdma DMA句柄
 * @param buffer_1 接收缓冲区1
 * @param buffer_2 接收缓冲区2
 * @param buffer_len 单个缓冲区长度（两个缓冲区大小需一致）
 * @details 配置串口为空闲中断模式，启用DMA双缓冲，自动切换缓冲区避免数据覆盖
 * @example QWF_double_buffer_dma_idle_Init(&huart6,&hdma6,vofa->rx.buffer[0],vofa->rx.buffer[1],sizeof(vofa->rx.buffer[0]));
 */
void QWF_double_buffer_dma_idle_Init(UART_HandleTypeDef* huart,DMA_HandleTypeDef*hdma,uint8_t*buffer_1,uint8_t* buffer_2,uint32_t buffer_len){
	#if Chip_Type==Chip_STM32F405RGT6
	huart->ReceptionType = HAL_UART_RECEPTION_TOIDLE;
	huart->RxEventType = HAL_UART_RXEVENT_IDLE;
	huart->RxXferSize    = buffer_len;
	 SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);//使能串口空闲中断
    __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);//使能DMA串口接收
	do __HAL_DMA_DISABLE(hdma);/*失效DMA*/
    while(hdma->Instance->CR & DMA_SxCR_EN);
	hdma->Instance->PAR = (uint32_t) & (huart->Instance->DR);
    hdma->Instance->M0AR = (uint32_t)(buffer_1);//内存缓冲区1
    hdma->Instance->M1AR = (uint32_t)(buffer_2);//内存缓冲区2
    hdma->Instance->NDTR = buffer_len*2;//数据长度
    SET_BIT(hdma->Instance->CR, DMA_SxCR_DBM);//使能双缓冲区
    __HAL_DMA_ENABLE(hdma);//使能DMA
	#elif Chip_Type==Chip_STM32H723VGT6
	huart->ReceptionType = HAL_UART_RECEPTION_TOIDLE;
	huart->RxXferSize    = buffer_len * 2;
	SET_BIT(huart->Instance->CR3,USART_CR3_DMAR);
	__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE); 
	do{
		__HAL_DMA_DISABLE(huart->hdmarx);
	}while(((DMA_Stream_TypeDef  *)huart->hdmarx->Instance)->CR & DMA_SxCR_EN);
	/* Configure the source memory Buffer address  */
	((DMA_Stream_TypeDef  *)huart->hdmarx->Instance)->PAR = (uint32_t)&huart->Instance->RDR;
	/* Configure the destination memory Buffer address */
	((DMA_Stream_TypeDef  *)huart->hdmarx->Instance)->M0AR = (uint32_t)buffer_1;
	/* Configure DMA Stream destination address */
	((DMA_Stream_TypeDef  *)huart->hdmarx->Instance)->M1AR = (uint32_t)buffer_2;
	/* Configure the length of data to be transferred from source to destination */
	((DMA_Stream_TypeDef  *)huart->hdmarx->Instance)->NDTR = buffer_len;
	/* Enable double memory buffer */
	SET_BIT(((DMA_Stream_TypeDef  *)huart->hdmarx->Instance)->CR, DMA_SxCR_DBM);
	/* Enable DMA */
	__HAL_DMA_ENABLE(huart->hdmarx);	
	#endif
	
}
/**
 * @brief 双缓冲DMA+空闲中断接收回调处理
 * @param huart 串口句柄
 * @param hdma DMA句柄
 * @param buffer_len 单个缓冲区长度
 * @param callback 用户自定义数据处理函数（参数为当前有效缓冲区指针）
 * @details 检测串口空闲中断，计算接收数据长度，切换DMA缓冲区并调用用户回调
 *       注意：需在串口空闲中断服务函数中调用此函数
 * @example QWF_double_buffer_dma_idle_callback(&huart6,&hdma6,sizeof(vofa->rx.buffer[0]),vofa_cal);
 */
void QWF_double_buffer_dma_idle_callback(UART_HandleTypeDef* huart,DMA_HandleTypeDef*hdma,uint32_t buffer_len,void (*callback)(uint8_t* pData)){
	#if Chip_Type==Chip_STM32F405RGT6
	if(huart->Instance->SR&UART_FLAG_RXNE){
	    __HAL_UART_CLEAR_IDLEFLAG(huart);//清除空闲中断标志（否则会一直不断进入中断）	
	}
	else if(huart->Instance->SR & UART_FLAG_IDLE)//串口空闲时执行
	{
    	static uint16_t this_time_rx_len = 0;
   		__HAL_UART_CLEAR_IDLEFLAG(huart);
    	if ((hdma->Instance->CR & DMA_SxCR_CT) == RESET)//缓冲区0
    	{ 
    	    //失效DMA或者停止DMA传输
    	    __HAL_DMA_DISABLE(hdma); //HAL_UART_DMAStop(&huart1);
    	    //获取接收数据长度
    	    this_time_rx_len = hdma->Instance->NDTR;
    	    //重新设定数据长度
    	    hdma->Instance->NDTR = buffer_len;
    	    //设定缓冲区1
    	    hdma->Instance->CR |= DMA_SxCR_CT;
    	    if((this_time_rx_len == buffer_len/2)||(buffer_len==0)){
				callback((uint8_t*)hdma->Instance->M0AR);
			}
    	}
    	else//缓冲区1
   		{
    	    //失效DMA
    	    __HAL_DMA_DISABLE(hdma); //HAL_UART_DMAStop(&huart1);
    	    //获取接收数据长度
    	    this_time_rx_len = hdma->Instance->NDTR;
    	    //重新设定数据长度
    	    hdma->Instance->NDTR = buffer_len;
    	    //设定缓冲区0
       		hdma->Instance->CR &= ~(DMA_SxCR_CT);
    	    if((this_time_rx_len == buffer_len/2)||(buffer_len==0)){
				callback((uint8_t*)hdma->Instance->M1AR);
			}
    	}
	}
	 __HAL_DMA_ENABLE(hdma);//开启dma
	#elif Chip_Type==Chip_STM32H723VGT6
    static uint16_t this_time_rx_len = 0;
	if(((((DMA_Stream_TypeDef  *)huart->hdmarx->Instance)->CR) & DMA_SxCR_CT ) == RESET){	
		/* Disable DMA */
		__HAL_DMA_DISABLE(huart->hdmarx);
		/* Switch Memory 0 to Memory 1*/
		((DMA_Stream_TypeDef  *)huart->hdmarx->Instance)->CR |= DMA_SxCR_CT;
		this_time_rx_len = ((DMA_Stream_TypeDef  *)huart->hdmarx->Instance)->NDTR;
		/* Reset the receive count */
		__HAL_DMA_SET_COUNTER(huart->hdmarx,buffer_len*2);
		/* Juge whether size is equal to the length of the received data */
		if(buffer_len == this_time_rx_len){
			/* Memory 0 data update to remote_ctrl*/
			callback((uint8_t*)((DMA_Stream_TypeDef  *)huart->hdmarx->Instance)->M0AR);	
		}
	}
	/* Current memory buffer used is Memory 1 */
	else{
		/* Disable DMA */
		__HAL_DMA_DISABLE(huart->hdmarx);
		/* Switch Memory 1 to Memory 0*/
		((DMA_Stream_TypeDef  *)huart->hdmarx->Instance)->CR &= ~(DMA_SxCR_CT);
		this_time_rx_len = ((DMA_Stream_TypeDef  *)huart->hdmarx->Instance)->NDTR;
		/* Reset the receive count */
		__HAL_DMA_SET_COUNTER(huart->hdmarx,buffer_len*2);
		if(buffer_len == this_time_rx_len){
			/* Memory 1 to data update to remote_ctrl*/
			callback((uint8_t*)((DMA_Stream_TypeDef  *)huart->hdmarx->Instance)->M1AR);	
		}
			
	}
	#endif	

}
